#include "TerrainManager.h"
#include "RingManager.h"

CachedTexture::CachedTexture(TILEDESC1 * td, UINT flag, UINT64 offset)
	:  m_td(td), m_flag(flag), m_offset(offset), m_unloadable(true), m_usageCount(0)
{}
CachedTexture::CachedTexture(ID3D11ShaderResourceView * texture)
	: m_td(nullptr), m_flag(0), m_offset(0), m_unloadable(false), m_usageCount(0)
{
	m_texture.Attach(texture);
}
CachedTexture::CachedTexture(ID3D11ShaderResourceView * texture, bool unloadable)
	: m_td(nullptr), m_flag(0), m_offset(0), m_unloadable(unloadable), m_usageCount(0)
{
	m_texture.Attach(texture);
}
CachedTexture::~CachedTexture() 
{
}
void CachedTexture::Unload()
{
	if (!m_unloadable || m_offset == NOTILE || m_offset == NOTILE64)
		return;
	if (++m_usageCount > 10000)
	{
		m_texture.Release();
		m_td->flags |= m_flag;
	}
}
void CachedTexture::Load(ID3D11ShaderResourceView * texture)
{
	if (!m_unloadable)
		return;
	m_texture.Attach(texture);
	m_td->flags &= ~m_flag;
	m_usageCount = 0;
}
UINT CachedTexture::GetOffset() { return m_offset; }

bool CachedTexture::IsUnloadable() { return m_unloadable; }

ID3D11ShaderResourceView ** CachedTexture::GetTexture()
{
	m_usageCount = 0;
	return &m_texture;
}

bool CachedTexture::IsLoaded() { return (m_texture.p != nullptr); }

void CachedTexture::Init(std::unique_ptr<CachedTexture> & dest, ID3D11ShaderResourceView * tex)
{
	if( tex || (dest && (DWORD)dest->m_offset == NOTILE && dest->m_offset == NOTILE64 ))
	{
		if (dest)
		{
			if (tex)
			{
				dest->Load(tex);
			}
		}
		else
		{
			if (tex)
			{
				dest.reset(new CachedTexture(tex, true));
			}
		}
	}
}

TileRenderMgr::TileRenderMgr( TerrainManager *_term, PlanetaryConfig *_CFG,  TILEMESH1 ***_TPL ) {
	term = _term;
	*_TPL = TPL;
	rmgr = NULL;

	VS_Tile = NULL;
	HS_Tile = NULL;
	DS_Tile = NULL;
	PS_Tile = NULL;

	CFG = _CFG;
	amgr = term->OBJ->AtmM;
	if( term->OBJ->RingData )	rmgr = term->OBJ->RingData->RM;

	aTerrainMicroTex = DisplacementMicroTex = NULL;

	bShowWireFrame = false;
}

TileRenderMgr::~TileRenderMgr() 
{
	for(auto it = SM.begin(); it != SM.end(); it++)
	{
		delete [] it->Name;
		delete [] it->Definition;
	}
	IL_TILEVERTEX_HIGH.Release();
	/*REL( VS_Tile );
	REL( HS_Tile );
	REL( DS_Tile );
	REL( PS_Tile );*/
	REL( DisplacementMicroTex );
	REL( aTerrainMicroTex );
}

void TileRenderMgr::UpdatePerFrameBuffers( float dist_scale, float coef_altitude, float luminance ) {
	//atmosphere params:
	ATM_FOG_PARAMS *ap = term->OBJ->GetAtmParams();
	cb_AtmParams cbuf;
	cbuf.FogColor = D3DXVECTOR3( ap->FogColor.r, ap->FogColor.g, ap->FogColor.b );
	cbuf.FogDensity = ap->FogDensity;
	cbuf.SunDir = D3DXVECTOR3( ap->SunDir.x, ap->SunDir.y, ap->SunDir.z );
	cbuf.Dispersion = ap->Dispersion;
	cbuf.SunAppRad = ap->SunAppRad;
	cbuf.Ambient0 = ap->Ambient0;
	cbuf.GlobalAmb = ap->GlobalAmb;
	cbuf.dist_scale = dist_scale;
	cbuf.PlanetPosW = D3DXVECTOR3( float( -1.0*term->OBJ->CPos.x ),  float( -1.0*term->OBJ->CPos.y ), float( -1.0*term->OBJ->CPos.z ) );
	cbuf.PlanetPosW /= 1000.0f;
	cbuf.exposure = luminance;
	iCtx->UpdateSubresource( CB_AtmParams, 0, NULL, &cbuf, 0, 0 );

	VSCB_per_frame vbuf;
	vbuf.VP = *SC->GetVP();
	vbuf.dist_scale = dist_scale;
	vbuf.coef_altitude = coef_altitude;
	vbuf.disp_microtex_range = CFG->DisplacementMicroTex_range;
	iCtx->UpdateSubresource( VSCB_Tile[0], 0, NULL, &vbuf, 0, 0 );

	PSCB_per_frame pbuf;
	pbuf.SunDir = cbuf.SunDir;
	pbuf.water_spower = 20.0f;
	D3DXVECTOR3 cAmbient = term->obj == SC->GetProxyBody() ? D3DXVECTOR3( 0.0f, 0.0f, 0.0f ) : *SC->GetAmbientColor();
	pbuf.Ambient = cAmbient;
	pbuf.coef_altitude = 1.0 - coef_altitude;
	iCtx->UpdateSubresource( PSCB_Tile[0], 0, NULL, &pbuf, 0, 0 );
	
	//================================================
	//			planetary eclipse shadows:
	//================================================

/*	if( CFG->bPlanetaryShadows ) {
		VECTOR3 tmp;
		cb_PlanetaryShadowParams sbuf;
		memset( &sbuf, 0, sizeof(sbuf) );

		oapiGetGlobalPos( oapiGetObjectByIndex( 0 ), &tmp );
		tmp = tmp - SC->CamPos - term->OBJ->CPos;
		sbuf.SunPosW = D3DXVECTOR3( float(tmp.x), float(tmp.y), float(tmp.z) );

		UINT idx = 0;
		vPlanet *P;
		CELOBJ *COBJ = SC->PLIST;
		for( UINT j = 0; j < SC->GObjCount; j++ ) {
			if( COBJ->type == OBJTP_PLANET && (P = (vPlanet*)COBJ[j].vo) != term->OBJ ) {
				tmp = P->CPos - term->OBJ->CPos;
				sbuf.Planet[idx].PosW = D3DXVECTOR3( float(tmp.x), float(tmp.y), float(tmp.z) );

				float ppdist = D3DXVec3Length( &sbuf.Planet[idx].PosW );
				float cosA = D3DXVec3Dot( &sbuf.SunPosW, &sbuf.Planet[idx].PosW )/(D3DXVec3Length( &sbuf.SunPosW )*ppdist);
				float _dist = cosA*ppdist;

				if( _dist > PLANETARY_SHADOW_DIST_LIMIT )	break;

				float SR = sqrt(ppdist*ppdist - _dist*_dist);
				if( SR > (P->rad + term->OBJ->rad) ) {
					sbuf.Planet[idx].Dispersion = P->AP.Dispersion;
					sbuf.Planet[idx].hasAtm = (CFG->AtmosphereType > 0 ? 1 : 0 );
					sbuf.Planet[idx].rad = P->rad;
					idx++;
				}
			}
		}
		sbuf.nplanet = idx;

		iCtx->UpdateSubresource( CB_PlanetShadowParams, 0, NULL, &sbuf, 0, 0 );
	}*/

	//================================================
	//			ring shadows:
	//================================================
/*
	if( CFG->bRingShadows && rmgr ) {
		cb_RingParams rbuf;
		rbuf.irad = float(rmgr->irad);
		rbuf.orad = float(rmgr->orad);
		rbuf.PosPW = D3DXVECTOR3( float(term->OBJ->CPos.x), float(term->OBJ->CPos.y), float(term->OBJ->CPos.z) );
		D3DXVec3TransformCoord( &rbuf.PosPW, &rbuf.PosPW, &term->RP.mWorld );
		rbuf.NormRW = rmgr->NormRW;//compute correctly !

		iCtx->UpdateSubresource( CB_RingParams, 0, NULL, &rmgr, 0, 0 );
	}*/
}

//=========================================================
//			L1-L4
//=========================================================

void TileRenderMgr::RenderSphere( UINT8 level, UINT npatch, TILEDESC1 *td, D3DXMATRIX *mWorld, float dist_scale, float luminance ) {
	const UINT VBOffset = 0;

	UpdatePerFrameBuffers( dist_scale, 0.0f, luminance );

	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetInputLayout( IL_TILEVERTEX_LOW );
	
	iCtx->VSSetShader( VS_Tile_Far[0], NULL, 0 );//!
	iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );

	iCtx->PSSetShader( PS_Tile, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 2, PSCB_Tile );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_AtmParams );
	if( CFG->bPlanetaryShadows )	iCtx->PSSetConstantBuffers( 3, 1, &CB_PlanetShadowParams );		//planet eclipse shadows
	if( rmgr && CFG->bRingShadows )	iCtx->PSSetConstantBuffers( 4, 1, &CB_RingParams );				//ring shadows

	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_TileTexture );

	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );

	PSCB_per_tile pbuf;
	pbuf.color_mult = 1.0f;
	pbuf.max_microtex_dist = CFG->maxMicroTexDist;

wireframe:

	for( UINT j = 0; j < npatch; j++ ) {
		TILEMESH1 &mesh = TPL[level][j];
		iCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &Stride_TILEVERTEX_LOW, &VBOffset );
		iCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );

		VSCB_per_tile vbuf;
		vbuf.W = *mWorld;
		vbuf.flags = 0;
		pbuf.flags = 0;
		iCtx->UpdateSubresource( VSCB_Tile[1], 0, NULL, &vbuf, 0, 0 );

		iCtx->PSSetShaderResources( 0, 1, td[j].tex->GetTexture() );
		pbuf.flags = 0;
		if( CFG->bSpecularMap && td[j].flags & 2 ) {
			pbuf.flags |= 1;
			if( td[j].ltex ) {
				pbuf.flags |= 2;
				iCtx->PSSetShaderResources( 1, 1, &td[j].ltex );
			}
		}
		if( CFG->bNormalMap && td[j].ntex ) {
			iCtx->PSSetShaderResources( 2, 1, td[j].ntex->GetTexture() );
			pbuf.flags |= 4;
		}
		iCtx->UpdateSubresource( PSCB_Tile[1], 0, NULL, &pbuf, 0, 0 );		

		iCtx->DrawIndexed( mesh.nidx, 0, 0 );
	}

	if( bShowWireFrame && pbuf.color_mult < 1.5f ) {
		iCtx->RSSetState( RS_CullBack_Wire );
		pbuf.color_mult = WIREFRAME_COLOR_MULT;
		goto wireframe;
	}
}

//=========================================================
//			L5-L7
//=========================================================

void TileRenderMgr::RenderLowResTiles( TILERENDERSPEC *TRS, UINT ntile, float dist_scale, float luminance ) {
	const UINT VBOffset = 0;

	UpdatePerFrameBuffers( dist_scale, 0.0f, luminance );

	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->IASetInputLayout( IL_TILEVERTEX_LOW );
	
	iCtx->VSSetShader( VS_Tile_Far[0], NULL, 0 );
	iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );	

	iCtx->PSSetShader( PS_Tile, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 2, PSCB_Tile );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_AtmParams );
	if( CFG->bPlanetaryShadows )	iCtx->PSSetConstantBuffers( 3, 1, &CB_PlanetShadowParams );		//planet eclipse shadows
	if( rmgr && CFG->bRingShadows )	iCtx->PSSetConstantBuffers( 4, 1, &CB_RingParams );				//ring shadows

	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_MicroTexture );

	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_NoDepth_NoStencil, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );

	VSCB_per_tile vbuf;
	PSCB_per_tile pbuf;
	pbuf.color_mult = 1.0f;
	pbuf.max_microtex_dist = CFG->maxMicroTexDist;

wireframe:

	for( UINT j = 0; j < ntile; j++ ) {

		TILEMESH1 &mesh = TPL[TRS[j].level][TRS[j].ilat];
		iCtx->IASetVertexBuffers( 0, 1, &mesh.VB, &Stride_TILEVERTEX_LOW, &VBOffset );
		iCtx->IASetIndexBuffer( mesh.IB, DXGI_FORMAT_R16_UINT, 0 );

		pbuf.flags = 0;
		vbuf.W = TRS[j].mWorld;
		vbuf.flags = TRS[j].hemisphere ? 1 : 0;
		iCtx->UpdateSubresource( VSCB_Tile[1], 0, NULL, &vbuf, 0, 0 );

		iCtx->PSSetShaderResources( 0, 1, TRS[j].Tex.tex->GetTexture() );
		if( CFG->bSpecularMap && TRS[j].flag & 2 ) {
			pbuf.flags |= 1;
			if( TRS[j].Tex.ltex ) {
				pbuf.flags |= 2;
				iCtx->PSSetShaderResources( 1, 1, &TRS[j].Tex.ltex );
			}
		}

		if( CFG->bNormalMap && TRS[j].Tex.ntex ) {
			iCtx->PSSetShaderResources( 2, 1, TRS[j].Tex.ntex->GetTexture() );//normal map
			pbuf.flags |= 4;
		}
		iCtx->UpdateSubresource( PSCB_Tile[1], 0, NULL, &pbuf, 0, 0 );

		iCtx->DrawIndexed( mesh.nidx, 0, 0 );
	}

	if( bShowWireFrame && pbuf.color_mult < 1.5f ) {
		iCtx->RSSetState( RS_CullBack_Wire );
		pbuf.color_mult = WIREFRAME_COLOR_MULT;
		goto wireframe;
	}
}

//=========================================================
//			L8-L18
//=========================================================

void TileRenderMgr::RenderHighResTiles( TILERENDERSPEC *TRS, UINT ntile, float dist_scale, float coef_altitude, float luminance ) {
	UINT j;
	const UINT VBOffset = 0;
	
	UpdatePerFrameBuffers( dist_scale, coef_altitude, luminance );

	iCtx->IASetInputLayout( IL_TILEVERTEX_HIGH );
	iCtx->IASetVertexBuffers( 0, 1, &VB_TPL, &Stride_TILEVERTEX_HIGH, &VBOffset );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
	iCtx->RSSetState( RS_CullBack_Solid );
	
	if( FL == 10 ) {
		iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
		iCtx->VSSetShader( VS_Tile, NULL, 0 );
		iCtx->VSSetSamplers( 0, 1, &SS_HeightMap );
		iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );		
	}
	else {
		iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_3_CONTROL_POINT_PATCHLIST );
		iCtx->VSSetShader( VS_Tile, NULL, 0 );
		iCtx->VSSetConstantBuffers( 0, 2, VSCB_Tile );

		iCtx->HSSetShader( HS_Tile, NULL, 0 );
		iCtx->HSSetConstantBuffers( 0, 1, &HSCB_Tile );
		iCtx->HSSetSamplers( 0, 1, &SS_HeightMap );
		if( DisplacementMicroTex ) {
			iCtx->HSSetSamplers( 1, 1, &SS_MicroTexture );
			iCtx->HSSetShaderResources( 1, 1, &DisplacementMicroTex );
		}

		iCtx->DSSetShader( DS_Tile, NULL, 0 );
		iCtx->DSSetConstantBuffers( 0, 2, VSCB_Tile );
	
		iCtx->DSSetSamplers( 0, 1, &SS_HeightMap );
		if( DisplacementMicroTex ) {
			iCtx->DSSetSamplers( 1, 1, &SS_MicroTexture );
			iCtx->DSSetShaderResources( 1, 1, &DisplacementMicroTex );
		}
	}

	iCtx->PSSetShader( PS_Tile, NULL, 0 );
	iCtx->PSSetConstantBuffers( 0, 2, PSCB_Tile );
	iCtx->PSSetConstantBuffers( 2, 1, &CB_AtmParams );
	if( CFG->bPlanetaryShadows )	iCtx->PSSetConstantBuffers( 3, 1, &CB_PlanetShadowParams );		//planet eclipse shadows
	if( CFG->bTerrainDetails_Low )
		iCtx->PSSetConstantBuffers( 5, 1, &CB_ForestMicro );										//forest microtextures
	if( rmgr && CFG->bRingShadows )	iCtx->PSSetConstantBuffers( 4, 1, &CB_RingParams );				//ring shadows

	iCtx->PSSetSamplers( 0, 1, &SS_TileTexture );
	iCtx->PSSetSamplers( 1, 1, &SS_MicroTexture );

	if( WaterMicroTex )		iCtx->PSSetShaderResources( 4, 1, &WaterMicroTex );
	if( aTerrainMicroTex )	iCtx->PSSetShaderResources( 5, 1, &aTerrainMicroTex );
//	if( rmgr && CFG->bRingShadows )	iCtx->PSSetShaderResources( 7, 1, &rmgr->tex );//texture !

	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );
	iCtx->OMSetDepthStencilState( DSS_Tile, 0 );

	//=========================================================
	//						TERRAIN RENDER
	//=========================================================
	
	VSCB_per_tile vbuf;
	HSCB_per_tile hbuf;
	PSCB_per_tile pbuf;
	pbuf.color_mult = 1.0f;
	pbuf.max_microtex_dist = CFG->maxMicroTexDist;

wireframe:

	for( j = 0; j < ntile; j++ ) {

		pbuf.flags = 0;
		vbuf.W = TRS[j].mWorld;
		vbuf.flags = 0;
		vbuf.coef_lat_0 = (float)(PI05*((double)TRS[j].ilat/(double)TRS[j].nlat));
		vbuf.coef_lat_1 = (float)(8.0/(double)TRS[j].nlat);
		vbuf.coef_lng = (float)((8.0*4.0*(double)TRS[j].nlat)/((double)TRS[j].nlng*(double)TRS[j].nlat));

		//gap between tile fix ...

		if( TRS[j].nlat > 10 ) {
			vbuf.cos_minlat = (float)(1000.0*cos((PI05*(double)TRS[j].ilat)/(double)TRS[j].nlat));
			vbuf.sin_minlat = (float)(1000.0*sin((PI05*(double)TRS[j].ilat)/(double)TRS[j].nlat));
		}
		else	vbuf.cos_minlat = vbuf.sin_minlat = 0.0f;

		//diffuse/specular texture coord range:
		vbuf.TexOff[0] = TRS[j].RANGE.range_tex.tumax - TRS[j].RANGE.range_tex.tumin;
		vbuf.TexOff[1] = TRS[j].RANGE.range_tex.tumin;
		vbuf.TexOff[2] = TRS[j].RANGE.range_tex.tvmax - TRS[j].RANGE.range_tex.tvmin;
		vbuf.TexOff[3] = TRS[j].RANGE.range_tex.tvmin;

		//=======================================================
		//			Normal Map
		//=======================================================
		if( CFG->bNormalMap && TRS[j].Tex.ntex /*coef_altitude < 0.95f*/ ) {
			iCtx->PSSetShaderResources( 2, 1, TRS[j].Tex.ntex->GetTexture() );//normal map
			pbuf.flags |= 4;

			//tex coord range for normal map
			vbuf.TexOff_norm[0] = TRS[j].RANGE.range_norm.tumax - TRS[j].RANGE.range_norm.tumin;
			vbuf.TexOff_norm[1] = TRS[j].RANGE.range_norm.tumin;
			vbuf.TexOff_norm[2] = TRS[j].RANGE.range_norm.tvmax - TRS[j].RANGE.range_norm.tvmin;
			vbuf.TexOff_norm[3] = TRS[j].RANGE.range_norm.tvmin;
		}

		//=======================================================
		//			Height Map
		//=======================================================
		if( CFG->bHeightMap && TRS[j].Tex.htex ) {
			if( FL == 10 )		iCtx->VSSetShaderResources( 0, 1, TRS[j].Tex.htex->GetTexture() );//VS if FL10
			else {
				iCtx->HSSetShaderResources( 0, 1, TRS[j].Tex.htex->GetTexture() );
				iCtx->DSSetShaderResources( 0, 1, TRS[j].Tex.htex->GetTexture() );

				HSCB_per_tile hbuf;
				hbuf.edge_tf = 2.0f;
				hbuf.max_inside_tf = 6.0f;
				hbuf.min_inside_tf = 1.0f;
				hbuf.coef_diff = 1.0f;

				iCtx->UpdateSubresource( HSCB_Tile, 0, NULL, &hbuf, 0, 0 );
			}

			vbuf.flags |= 2;

			//tex coord range for heightmap
			vbuf.TexOff_height[0] = TRS[j].RANGE.range_height.tumax - TRS[j].RANGE.range_height.tumin;
			vbuf.TexOff_height[1] = TRS[j].RANGE.range_height.tumin;
			vbuf.TexOff_height[2] = TRS[j].RANGE.range_height.tvmax - TRS[j].RANGE.range_height.tvmin;
			vbuf.TexOff_height[3] = TRS[j].RANGE.range_height.tvmin;

			//used for normal and tangents of heightmapped mesh
			vbuf.dist_lat = (term->OBJ->rad*PI05)/(128.0f*(double)8192/*TRS[j].nlat*/);
			vbuf.dist_lng = (term->OBJ->rad*PI2)/(128.0f*(double)32768/*TRS[j].nlng*/);
			vbuf.dist_lat *= vbuf.dist_lat;
			vbuf.dist_lng *= vbuf.dist_lng;
		}

		//=======================================================
		//			Land Cover Map
		//=======================================================
		if( /*CFG->bDetailedTerrain*/ 1 && TRS[j].Tex.ctex ) {
			iCtx->PSSetShaderResources( 3, 1, &TRS[j].Tex.ctex );//land cover map
			pbuf.flags |= 16;

			//tex coord range for heightmap
			vbuf.TexOff_lcover[0] = TRS[j].RANGE.range_lcover.tumax - TRS[j].RANGE.range_lcover.tumin;
			vbuf.TexOff_lcover[1] = TRS[j].RANGE.range_lcover.tumin;
			vbuf.TexOff_lcover[2] = TRS[j].RANGE.range_lcover.tvmax - TRS[j].RANGE.range_lcover.tvmin;
			vbuf.TexOff_lcover[3] = TRS[j].RANGE.range_lcover.tvmin;
		}

		iCtx->UpdateSubresource( VSCB_Tile[1], 0, NULL, &vbuf, 0, 0 );

		iCtx->PSSetShaderResources( 0, 1, TRS[j].Tex.tex->GetTexture() );
		if( CFG->bSpecularMap && TRS[j].flag & 2 ) {
			pbuf.flags |= 1;
			if( WaterMicroTex )	pbuf.flags |= 8;
			if( TRS[j].Tex.ltex ) {
				pbuf.flags |= 2;
				iCtx->PSSetShaderResources( 1, 1, &TRS[j].Tex.ltex );
			}
		}

		iCtx->UpdateSubresource( PSCB_Tile[1], 0, NULL, &pbuf, 0, 0 );

		UINT R = (coef_altitude == 0.0f ? 0 : ( (TRS[j].td->amax - TRS[j].td->amin) > 0.35f*CFG->maxTerrainElevation ? 2 : 1 ));
		UINT P = TRS[j].ilat+1 == TRS[j].nlat;

		iCtx->DrawIndexed( TRES[R].nidx[P], TRES[R].sidx[P], TRES[R].svtx[P] );
	}

	if( bShowWireFrame && pbuf.color_mult < 1.5f ) {
		iCtx->RSSetState( RS_CullBack_Wire );
		pbuf.color_mult = WIREFRAME_COLOR_MULT;
		goto wireframe;
	}

	if( FL == 11 ) {
		iCtx->HSSetShader( NULL, NULL, 0 );
		iCtx->DSSetShader( NULL, NULL, 0 );
	}
}

void AddShaderMacro(std::vector<D3D10_SHADER_MACRO>& sm, const D3D10_SHADER_MACRO& macro)
{
	D3D10_SHADER_MACRO newMacro;
	auto str = new char[strlen(macro.Name) + 1];
	strcpy(str, macro.Name);
	newMacro.Name = str;
	str = new char[strlen(macro.Definition) + 1];
	strcpy(str, macro.Definition);
	newMacro.Definition = str;
	sm.push_back(newMacro);
}

float TileRenderMgr::InitResources( float **flat_disp_data, float **coarse_disp_data, D3D11Client * client ) {
	//loads microtextures and compiles shaders for current planet cofig
	D3D10_SHADER_MACRO macro;
	HRESULT hr;
	BYTE *data;
	UINT dim = 0, ntex = 0;
	char fname[256], cpath[256], line[32][32];
	memset( &line, 0, 32*32 );

	D3D11_TEXTURE2D_DESC tdesc2;
	ID3D11Texture2D *tex2;
	D3D11_TEXTURE3D_DESC tdesc3;
	ID3D11Texture3D *tex3;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	D3D11_SUBRESOURCE_DATA sdata;
	std::vector<BYTE*> TEX;
	UINT twidth[2] = { 0 }, theight[2] = { 0 }, nmip = 0;

	//distance where microtexture still sampled
	macro.Name = "MICROTEX_MAX_DIST";
	sprintf( line[0], "%0.1ff", CFG->maxMicroTexDist );
	macro.Definition = line[0];
	AddShaderMacro(SM, macro );

	macro.Name = "MIN_ANGLE";
	sprintf( line[1], "%0.1ff", CFG->MicroTexTransAngles[0] );
	macro.Definition = line[1];
	AddShaderMacro(SM, macro );

	macro.Name = "MAX_ANGLE";
	sprintf( line[2], "%0.1ff", CFG->MicroTexTransAngles[1] );
	macro.Definition = line[2];
	AddShaderMacro(SM, macro );

	//==================================================================
	//					Displacement microtextures
	//==================================================================
#pragma region disp_microtex
	if( CFG->bDisplacementMicroTex ) {
		*flat_disp_data = *coarse_disp_data = NULL;

		sprintf( fname, "%s%s_micro_disp_flat.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( (BYTE**)flat_disp_data, cpath, twidth[0], theight[0] ) )
			TEX.push_back( (BYTE*)*flat_disp_data );

		sprintf( fname, "%s%s_micro_disp_coarse.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( (BYTE**)coarse_disp_data, cpath, twidth[1], theight[1] ) ) {

			if( twidth[0] != twidth[1] || theight[0] != theight[1] ) {
				oapiWriteLog( "Error: incorrect dimesions of displacement microtextures." );
				delete [ ] *flat_disp_data;
				delete [ ] *coarse_disp_data;
				TEX.clear();
			}
			else
				TEX.push_back( (BYTE*)*coarse_disp_data );
		}

		//at least one of disp. microtextures loaded
		if( TEX.size() ) {

			int size = TEX.size();

			data = new BYTE [twidth[0]*theight[0]*TEX.size()];
			memcpy( data, *flat_disp_data, twidth[0]*theight[0] );
			memcpy( data + twidth[0]*theight[0], *coarse_disp_data, twidth[0]*theight[0] );

			memset( &tdesc3, 0, sizeof(tdesc3) );
			tdesc3.BindFlags = D3D11_BIND_SHADER_RESOURCE;
			tdesc3.CPUAccessFlags = 0;
			tdesc3.Depth = TEX.size();
			tdesc3.Format = DXGI_FORMAT_R8_UNORM;
			tdesc3.Height = twidth[0];
			tdesc3.Width = theight[0];
			tdesc3.MipLevels = 1;
			tdesc3.MiscFlags = 0;
			tdesc3.Usage = D3D11_USAGE_IMMUTABLE;

			memset( &sdata, 0, sizeof(sdata) );
			sdata.pSysMem = data;
			sdata.SysMemPitch = twidth[0];
			sdata.SysMemSlicePitch = twidth[0]*theight[0];

			if( S_OK == Dev->CreateTexture3D( &tdesc3, &sdata, &tex3 ) ) {

				memset( &srvdesc, 0, sizeof(srvdesc) );
				srvdesc.Format = DXGI_FORMAT_R8_UNORM;
				srvdesc.Texture3D.MipLevels = 1;
				srvdesc.Texture3D.MostDetailedMip = 0;
				srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;

				if( S_OK == Dev->CreateShaderResourceView( tex3, &srvdesc, &DisplacementMicroTex ) )
				{
				}
				REL( tex3 );
			}

			TEX.clear();
			delete [ ] data;
		}
	}

	if( DisplacementMicroTex ) {
		macro.Name = "DISPLACEMENT_HEIGHT_DIM_RATIO";
		sprintf( line[6], "%0.1ff", (float(twidth[0])/65.0f)*CFG->DisplacementMicroTex_mult );
		macro.Definition = line[6];
		AddShaderMacro(SM, macro );

		macro.Name = "DISPLACEMENT_MICROTEX_MULT";
		sprintf( line[3], "%0.1ff", CFG->DisplacementMicroTex_mult );
		macro.Definition = line[3];
		SM.push_back( macro );

		macro.Name = "DISPLACEMENT_MICROTEX_PSTRIDE";
		sprintf( line[4], "%0.1ff", float((term->OBJ->rad*2.0*PI)/double(8192*twidth[0]*CFG->DisplacementMicroTex_mult)) );
		macro.Definition = line[4];
		AddShaderMacro(SM, macro );

		macro.Name = "DISPLACEMENT_MICROTEX_DELTA";
		sprintf( line[5], "%0.1ff", float(1.0/(twidth[0]*CFG->DisplacementMicroTex_mult)) );
		macro.Definition = line[5];
		AddShaderMacro(SM, macro );
	}
	else {
		macro.Name = "DISPLACEMENT_HEIGHT_DIM_RATIO";
		macro.Definition = "1.0f";
		AddShaderMacro(SM, macro );

		macro.Name = "DISPLACEMENT_MICROTEX_PSTRIDE";
		macro.Definition = "1.0f";
		AddShaderMacro(SM, macro );
	}
#pragma endregion

	//==================================================================
	//					Diffuse microtextures
	//==================================================================
#pragma region diffuse and normal microtex loading

	//microtexture multipliers
	macro.Name = "FLAT_MICROTEX_MULT";
	sprintf( line[7], "%0.1ff", CFG->TerrainFlatMicroTex_mult );
	macro.Definition = line[7];
	AddShaderMacro(SM, macro );

	macro.Name = "COARSE_MICROTEX_MULT";
	sprintf( line[8], "%0.1ff", CFG->TerrainCoarseMicroTex_mult );
	macro.Definition = line[8];
	AddShaderMacro(SM, macro );

	twidth[0] = theight[0] = 0;
	memset( &sdata, 0, sizeof(sdata) );
	if( CFG->bDiffuseMicroTex ) {

		sprintf( fname, "%s%s_micro_flat.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
			if( !AddTexture( TEX, data, twidth, theight, nmip ) )
				goto skip;
			else {
				macro.Name = "FLAT_DIFFUSE_MICROTEX_IDX";
				sprintf( line[9], "%d", TEX.size()-1 );
				macro.Definition = line[9];
				AddShaderMacro(SM, macro );
			}
		}

		sprintf( fname, "%s%s_micro_coarse.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
			if( !AddTexture( TEX, data, twidth, theight, nmip ) )
				goto skip;
			else {
				macro.Name = "COARSE_DIFFUSE_MICROTEX_IDX";
				sprintf( line[10], "%d", TEX.size()-1 );
				macro.Definition = line[10];
				AddShaderMacro(SM, macro );
			}
		}
	}

	//==================================================================
	//					Normal or bump microtextures
	//==================================================================
	if( CFG->bNormalMicroTex ) {

		sprintf( fname, "%s%s_micro_flat_norm.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
			if( !AddTexture( TEX, data, twidth, theight, nmip ) )
				goto skip;
			else {
				macro.Name = "FLAT_NORMAL_MICROTEX_IDX";
				sprintf( line[11], "%d", TEX.size()-1 );
				macro.Definition = line[11];
				AddShaderMacro(SM, macro );
			}
		}

		sprintf( fname, "%s%s_micro_coarse_norm.dds", TERRAIN_TEXTURE_FOLDER, term->objname );
		if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
			if( !AddTexture( TEX, data, twidth, theight, nmip ) )
				goto skip;
			else {
				macro.Name = "COARSE_NORMAL_MICROTEX_IDX";
				sprintf( line[12], "%d", TEX.size()-1 );
				macro.Definition = line[12];
				AddShaderMacro(SM, macro );
			}
		}
	}
#pragma endregion
	//==================================================================
	//					Low details diffuse microtextures
	//==================================================================
#pragma region diffuse and normal low-details microtextures
	if( CFG->bTerrainDetails_Low ) {		

		bool any = false;
		for( int j = 0; j < CFG->CTP.size(); j++ ) {

			//"forest" diffuse microtexture (range ~150 km)
			sprintf( fname, "%s%s_%d_low.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
			if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
				if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;
				else {
					CFG->CTP[j].cidx = j;
					CFG->CTP[j].flags |= 1;
					any = true;
				}
			}

			//"forest" normal microtexture (range ~150 km)
			sprintf( fname, "%s%s_%d_low_norm.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
			if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
				if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;
				else {
					CFG->CTP[j].cidx = CFG->CTP[j].flags & 1 ? j : j-1;
					CFG->CTP[j].flags |= 2;
					any = true;
				}
			}

			//land cover specific diffuse ground microtexture (range ~10 km)
			sprintf( fname, "%s%s_%d_low_micro.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
			if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
				if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;
				else {
					CFG->CTP[j].midx = j;
					CFG->CTP[j].flags |= 4;//"has non-default diffuse microtexture"
					any = true;
				}
			}

			//land cover specific normal ground microtexture (range ~10 km)
			sprintf( fname, "%s%s_%d_low_micro_norm.dds", TERRAIN_TEXTURE_FOLDER, term->objname, j );
			if( gc->TexturePath( fname, cpath ) && ReadTexture( &data, cpath, twidth[0], theight[0] ) ) {
				if( !AddTexture( TEX, data, twidth, theight, nmip ) )	goto skip;
				else {
					CFG->CTP[j].midx = CFG->CTP[j].flags & 4 ? j : j-1;
					CFG->CTP[j].flags |= 8;//"has non-default microtexture"
					any = true;
				}
			}
		}

		if( any ) {
			macro.Name = "LOW_DETAILS_MICROTEX_MULT";
			sprintf( line[13], "%0.1ff", CFG->TerrainLowDetailsMicroTex_mult );
			macro.Definition = line[13];
			AddShaderMacro(SM, macro );
		}
	}
#pragma endregion

	if( TEX.size() ) {

		memset( &tdesc2, 0, sizeof(tdesc2) );
		tdesc2.ArraySize = TEX.size();
		tdesc2.BindFlags = D3D11_BIND_SHADER_RESOURCE;
		tdesc2.CPUAccessFlags = 0;
		tdesc2.Format = DXGI_FORMAT_BC1_UNORM;
		tdesc2.Height = twidth[0];
		tdesc2.Width = theight[0];
		tdesc2.MipLevels = nmip;
		tdesc2.MiscFlags = 0;
		tdesc2.SampleDesc.Count = 1;
		tdesc2.Usage = D3D11_USAGE_DEFAULT;

		if( S_OK == Dev->CreateTexture2D( &tdesc2, NULL, &tex2 ) ) {

			memset( &srvdesc, 0, sizeof(srvdesc) );
			srvdesc.Format = DXGI_FORMAT_BC1_UNORM;
			srvdesc.Texture2DArray.ArraySize = TEX.size();
			srvdesc.Texture2DArray.FirstArraySlice = 0;
			srvdesc.Texture2DArray.MipLevels = nmip;
			srvdesc.Texture2DArray.MostDetailedMip = 0;
			srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;

			if( S_OK == Dev->CreateShaderResourceView( tex2, &srvdesc, &aTerrainMicroTex ) ) {
				for( int j = 0; j < TEX.size(); j++ )
					iCtx->UpdateSubresource( tex2, j*nmip, NULL, TEX[j], twidth[0]/2, 0 );
				D3DX11FilterTexture( iCtx, tex2, 0, D3DX11_FILTER_LINEAR );
				
			}
			REL( tex2 );
		}
		else
			oapiWriteLog( "Error: terrain microtexture array creation failed. All textures must be in DXT1 format." );

		for( int j = 0; j < TEX.size(); j++ )	delete [ ] TEX[j];
		TEX.clear();

		macro.Name = "MICROTEXTURE";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}
skip:

#pragma region shader compilation
	
	macro.Name = "MAXCTYPE";
	sprintf( line[14], "%d", CFG->CTP.size() );
	macro.Definition = line[14];
	AddShaderMacro(SM, macro );

	macro.Name = "HEIGHT_SCALE";
	sprintf( line[15], "%0.1ff", term->HeightScale );
	macro.Definition = line[15];
	AddShaderMacro(SM, macro );

	//atmosphere type:
	switch( CFG->AtmosphereType ) {
	case NO_ATMOSPHERE:
		macro.Name = "NO_ATMOSPHERE";
		break;	
	}
	macro.Definition = "1";
	AddShaderMacro(SM, macro );

	//feature level:
	macro.Name = Dev->GetFeatureLevel() == D3D_FEATURE_LEVEL_11_0 ? "FL11" : "FL10";
	macro.Definition = "1";
	AddShaderMacro(SM, macro );

	if( CFG->bSpecularMap ) {
		macro.Name = "SPECULAR_MAP";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}

	if( CFG->bNormalMap ) {
		macro.Name = "NORMAL_MAP";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}

	//terrain shadows:
	if( CFG->bTerrainShadows ) {
		macro.Name = "TERRAIN_SHADOWS";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}

	//planetary shadows:
	if( CFG->bPlanetaryShadows ) {
		macro.Name = "PLANETARY_SHADOWS";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}

	//ring shadows:
	if( CFG->bRingShadows ) {
		macro.Name = "RING_SHADOWS";
		macro.Definition = "1";
		AddShaderMacro(SM, macro );
	}

	macro.Name = NULL;
	macro.Definition = NULL;
	SM.push_back( macro );

	ID3DBlob *SBlob, *EBlob;

	GET_SHADER_DEFINES(client, "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", "VS_Tile_Near", VS_Tile, SM.data());

	if( !IL_TILEVERTEX_HIGH ) 
	{
		D3D11_INPUT_ELEMENT_DESC iedesc_high[ ] = {
			{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR", 0, DXGI_FORMAT_R32_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }
		};
		client->CreateInputLayout(iedesc_high, VS_Tile, IL_TILEVERTEX_HIGH);
		//Dev->CreateInputLayout( iedesc_high, 3, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_TILEVERTEX_HIGH );
	}

	/*::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", &SM[0], NULL, "VS_Tile_Near", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	if( !IL_TILEVERTEX_HIGH ) {
		D3D11_INPUT_ELEMENT_DESC iedesc_high[ ] = {
			{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
			{ "COLOR", 0, DXGI_FORMAT_R32_UINT, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0 }
		};
		Dev->CreateInputLayout( iedesc_high, 3, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_TILEVERTEX_HIGH );
	}
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Tile ) );
	REL( SBlob );
	REL( EBlob );*/

	if( FL == 11 ) 
	{
		GET_SHADER_DEFINES(client, "Modules\\D3D11Shaders\\Planet\\HSDS_Planet1.fx", "HS_Tile", HS_Tile, SM.data());
		/*::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\HSDS_Planet1.fx", &SM[0], NULL, "HS_Tile", "hs_5_0", ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
		ShowShaderCompilationError( hr, EBlob );
		HR( Dev->CreateHullShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &HS_Tile ) );
		REL( SBlob );
		REL( EBlob );*/
		
		GET_SHADER_DEFINES(client, "Modules\\D3D11Shaders\\Planet\\HSDS_Planet1.fx", "DS_Tile", DS_Tile, SM.data());
		/*::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\HSDS_Planet1.fx", &SM[0], NULL, "DS_Tile", "ds_5_0", ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
		ShowShaderCompilationError( hr, EBlob );
		HR( Dev->CreateDomainShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &DS_Tile ) );
		REL( SBlob );
		REL( EBlob );*/
	}

	GET_SHADER_DEFINES(client, "Modules\\D3D11Shaders\\Planet\\PS_Planet1.fx", "PS_Tile", PS_Tile, SM.data());

	/*::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\PS_Planet1.fx", &SM[0], NULL, "PS_Tile", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Tile ) );
	REL( SBlob );
	REL( EBlob );*/

	//SM.clear();

#pragma endregion
	return 0.0f;
}

bool TileRenderMgr::AddTexture( std::vector<BYTE*> &TEX, BYTE *data, UINT *twidth, UINT *theight, UINT &nmip ) {
	D3D11_SUBRESOURCE_DATA sdata;

	//check dimesions
	if( twidth[0] != twidth[1] || theight[0] != theight[1] ) {
		if( !twidth[1] && !theight[1] ) {
			//first array element
			twidth[1] = twidth[0];
			theight[1] = theight[0];
		}
		else {
			oapiWriteLog( "Error: dimensions of microtextures must be equal to each other." );
			for( int j = 0; j < CFG->CTP.size(); j++ )	CFG->CTP[j].flags = 0;
			for( int j = 0; j < TEX.size(); j++ )	delete [ ] TEX[j];
			TEX.clear();
			return false;
		}
	}

	TEX.push_back( data );
	if( !nmip )		GetMipMapNumber( twidth[0], theight[0], nmip );
	return true;
}

bool ReadTexture( BYTE **data, char *fname, UINT &twidth, UINT &theight ) {

	FILE *file;
	if( !fname || !(file = fopen( fname, "rb" )) )	return 0;

	DWORD key;
	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) ) {
		fclose( file );
		return false;
	}

	DDS_HEADER HDR;
	fread( &HDR, sizeof(HDR), 1, file );
	twidth = HDR.dwWidth;
	theight = HDR.dwHeight;

	*data = new BYTE [HDR.dwPitchOrLinearSize];
	fread( *data, 1, HDR.dwPitchOrLinearSize, file );
	fclose( file );
	return true;
}

bool GetMipMapNumber( UINT w, UINT h, UINT &nmip ) {
	UINT maxd = max( w, h );
	nmip = 0;
	for( float d = (float)maxd; d > 0.98f; d /= 2.0f )
		nmip++;
	return true;
}

//============================================================================
//	
//============================================================================

TILEVERTEX_LOW _TVTX_LOW( D3DXVECTOR3 pos, D3DXVECTOR3 norm, float tu, float tv );

UINT
	TileRenderMgr::FL = 10;

CComPtrEx<ID3D11InputLayout> TileRenderMgr::IL_TILEVERTEX_HIGH;
ID3D11InputLayout
	*TileRenderMgr::IL_TILEVERTEX_LOW	= NULL;
	//*TileRenderMgr::IL_TILEVERTEX_HIGH	= NULL;
ID3D11VertexShader
	*TileRenderMgr::VS_Tile_Far[2]		= { NULL };
ID3D11SamplerState
	*TileRenderMgr::SS_HeightMap		= NULL,
	*TileRenderMgr::SS_TileTexture		= NULL,
	*TileRenderMgr::SS_MicroTexture		= NULL;
ID3D11Buffer
	*TileRenderMgr::VB_TPL				= NULL,
	*TileRenderMgr::IB					= NULL,
	*TileRenderMgr::CB_AtmParams		= NULL,
	*TileRenderMgr::CB_PlanetShadowParams	= NULL,
	*TileRenderMgr::CB_RingParams		= NULL,
	*TileRenderMgr::VSCB_Tile[2]		= { NULL },
	*TileRenderMgr::PSCB_Tile[2]		= { NULL },
	*TileRenderMgr::HSCB_Tile			= NULL;
ID3D11ShaderResourceView
	*TileRenderMgr::WaterMicroTex		= NULL;
ID3D11DepthStencilState
	*TileRenderMgr::DSS_Tile			= NULL;
TILEMESH1
	TileRenderMgr::TPL_1,
	TileRenderMgr::TPL_2,
	TileRenderMgr::TPL_3,
	TileRenderMgr::TPL_4[2],
	TileRenderMgr::TPL_5,
	TileRenderMgr::TPL_6[2],
	TileRenderMgr::TPL_7[4];
TileRenderMgr::TILE_RES_LVL
	TileRenderMgr::TRES[3];
UINT
	TileRenderMgr::Stride_TILEVERTEX_LOW = 44,
	TileRenderMgr::Stride_TILEVERTEX_HIGH = 16;
TILEMESH1 *TileRenderMgr::TPL[8] = { NULL, &TPL_1, &TPL_2, &TPL_3, TPL_4, &TPL_5, TPL_6, TPL_7 };
UINT TileRenderMgr::patchidx[9] = { 0, 1, 2, 3, 5, 13, 37, 137, 501 };
UINT TileRenderMgr::NLAT[9] = { 0, 1, 1, 1, 1, 1, 2, 4, 8 };
UINT
	TileRenderMgr::NLNG5[1] = { 4 },
	TileRenderMgr::NLNG6[2] = { 8, 4 },
	TileRenderMgr::NLNG7[4] = { 16, 16, 12, 6 },
	TileRenderMgr::NLNG8[8] = { 32, 32, 30, 28, 24, 18, 12, 6 }, 
	*TileRenderMgr::NLNG[9] = { NULL, NULL, NULL, NULL, NULL, NLNG5, NLNG6, NLNG7, NLNG8 };

void TileRenderMgr::GlobalInit( ID3D11Buffer **_CB_AtmParams, ID3D11Buffer **_CB_PlanetShadowParams ) {
	DWORD j;
	HRESULT hr;
	ID3DBlob *SBlob = NULL, *EBlob = NULL;

	FL = Dev->GetFeatureLevel() == D3D_FEATURE_LEVEL_11_0 ? 11 : 10;

	Stride_TILEVERTEX_LOW = 44;
	Stride_TILEVERTEX_HIGH = 20;

	//===================================================
	//				Basic Shaders
	//===================================================

	D3D10_SHADER_MACRO macro;
	std::vector<D3D10_SHADER_MACRO> mlist;

	if( FL == 11 ) {
		macro.Name = "FL11";
		macro.Definition = "1";
		mlist.push_back(  macro );
	}

	UINT dim, nvtx, nidx;
	memset( &TRES, 0, sizeof(TILE_RES_LVL)*3 );
	//================================================================================
	//						Tiles without heightmaps
	//			flat terrain or altitude above normal/height map transition
	//================================================================================		
	dim = 12;
	TRES[0].vdim = dim;
	TRES[0].edge_tf[0] = 1.0f;
	TRES[0].edge_tf[1] = 1.0f;
	TRES[0].inside_tf[0] = 1.0f;
	TRES[0].inside_tf[1] = 1.0f;

	TRES[0].svtx[0] = 0;
	TRES[0].nvtx[0] = (dim+1)*(dim+1);
	TRES[0].sidx[0] = 0;
	TRES[0].nidx[0] = 6*dim*dim;

	TRES[0].svtx[1] = TRES[0].nvtx[0];
	TRES[0].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[0].sidx[1] = TRES[0].nidx[0];
	TRES[0].nidx[1] = 3*dim*dim;

	//================================================================================
	//					Tiles with non-rough heightmaps (water, valleys)
	//			flat terrain or altitude above normal/height map transition
	//================================================================================
	dim = (FL == 11 ? 16 : 34);
	TRES[1].vdim = dim;
	TRES[1].edge_tf[0] = 1.0f;
	TRES[1].edge_tf[1] = 3.0f;
	TRES[1].inside_tf[0] = 1.0f;
	TRES[1].inside_tf[1] = 3.0f;

	TRES[1].svtx[0] = TRES[0].svtx[1] + TRES[0].nvtx[1];
	TRES[1].nvtx[0] = (dim+1)*(dim+1);
	TRES[1].sidx[0] = TRES[0].sidx[1] + TRES[0].nidx[1];
	TRES[1].nidx[0] = 6*dim*dim;

	TRES[1].svtx[1] = TRES[1].svtx[0] + TRES[1].nvtx[0];
	TRES[1].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[1].sidx[1] = TRES[1].sidx[0] + TRES[1].nidx[0];
	TRES[1].nidx[1] = 3*dim*dim;

	//================================================================================
	//					Tiles with coarse heightmaps (mountains)
	//			flat terrain or altitude above normal/height map transition
	//================================================================================
	dim = (FL == 11 ? 16 : 66);
	TRES[2].vdim = dim;
	TRES[2].edge_tf[0] = 3.0f;
	TRES[2].edge_tf[1] = 5.0f;
	TRES[2].inside_tf[0] = 1.0f;
	TRES[2].inside_tf[1] = 4.0f;

	TRES[2].svtx[0] = TRES[1].svtx[1] + TRES[1].nvtx[1];
	TRES[2].nvtx[0] = (dim+1)*(dim+1);
	TRES[2].sidx[0] = TRES[1].sidx[1] + TRES[1].nidx[1];
	TRES[2].nidx[0] = 6*dim*dim;

	TRES[2].svtx[1] = TRES[2].svtx[0] + TRES[2].nvtx[0];
	TRES[2].nvtx[1] = (dim+1)*(dim+1) - ((dim+1)*dim)/2;
	TRES[2].sidx[1] = TRES[2].sidx[0] + TRES[2].nidx[0];
	TRES[2].nidx[1] = 3*dim*dim;

	nvtx = TRES[2].svtx[1] + TRES[2].nvtx[1];
	nidx = TRES[2].sidx[1] + TRES[2].nidx[1];
	//================================================================================
	//================================================================================

	macro.Name = "DISPLACEMENT_HEIGHT_DIM_RATIO";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "DISPLACEMENT_MICROTEX_PSTRIDE";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "MIN_ANGLE";
	macro.Definition = "0.0f";
	mlist.push_back( macro );

	macro.Name = "MAX_ANGLE";
	macro.Definition = "1.0f";
	mlist.push_back( macro );

	macro.Name = "HEIGHT_SCALE";
	macro.Definition = "1";
	mlist.push_back( macro );

	macro.Name = NULL;
	macro.Definition = NULL;
	mlist.push_back( macro );

	D3D11_INPUT_ELEMENT_DESC iedesc_low[ ] = {
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 36, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", &mlist[0], NULL, "VS_Tile_Far", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateInputLayout( iedesc_low, 4, SBlob->GetBufferPointer(), SBlob->GetBufferSize(), &IL_TILEVERTEX_LOW ) );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Tile_Far[0] ) );
	REL( SBlob );
	REL( EBlob );

	mlist[mlist.size()-1].Name = "BASIC_ATMOSPHERE";
	mlist[mlist.size()-1].Definition = "1";
	macro.Name = NULL;
	macro.Definition = NULL;
	mlist.push_back( macro );

	::CompileFromFile( "Modules\\D3D11Shaders\\Planet\\VS_Planet1.fx", &mlist[0], NULL, "VS_Tile_Far", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Tile_Far[1] ) );
	REL( SBlob );
	REL( EBlob );

	//===================================================
	//				Constant buffers
	//===================================================

	D3D11_BUFFER_DESC bdesc;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 80;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VSCB_Tile[0] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 160;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &VSCB_Tile[1] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 64;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, _CB_AtmParams ) );//_CB_AtmParams is in TerrainManager
	CB_AtmParams = *_CB_AtmParams;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 48;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, _CB_PlanetShadowParams ) );//_CB_AtmParams is in TerrainManager
	CB_PlanetShadowParams = *_CB_PlanetShadowParams;

	if( FL == 11 ) {
		memset( &bdesc, 0, sizeof(bdesc) );
		bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
		bdesc.ByteWidth = 16;
		bdesc.CPUAccessFlags = 0;
		bdesc.MiscFlags = 0;
		bdesc.StructureByteStride = 0;
		bdesc.Usage = D3D11_USAGE_DEFAULT;

		HR( Dev->CreateBuffer( &bdesc, NULL, &HSCB_Tile ) );
	}

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 32;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &PSCB_Tile[0] ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 16;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_DEFAULT;

	HR( Dev->CreateBuffer( &bdesc, NULL, &PSCB_Tile[1] ) );

	//===================================================
	//				Texture Samplers
	//===================================================

	D3D11_SAMPLER_DESC sdesc;

	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_EQUAL;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	Dev->CreateSamplerState( &sdesc, &SS_HeightMap );

	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_EQUAL;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	HR( Dev->CreateSamplerState( &sdesc, &SS_TileTexture ) );

	//water and forest microtextures
	memset( &sdesc, 0, sizeof(sdesc) );
	sdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;//<= cfg
	sdesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	sdesc.MaxAnisotropy = 1;
	sdesc.MipLODBias = 0;
	sdesc.MinLOD = 0;
	sdesc.MaxLOD = D3D11_FLOAT32_MAX;

	HR( Dev->CreateSamplerState( &sdesc, &SS_MicroTexture) );

	//===================================================
	//				ShadowVolume texture
	//===================================================

	ID3D11Texture2D *tex;
	D3D11_TEXTURE2D_DESC tdesc;
	D3D11_RENDER_TARGET_VIEW_DESC rtvdesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;

/*	memset( &tdesc, 0, sizeof(tdesc) );
	tdesc.ArraySize = 1;
	tdesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	tdesc.CPUAccessFlags = 0;
	tdesc.Format = DXGI_FORMAT_R32_FLOAT;
	tdesc.Height = cfg->cHeight;
	tdesc.MipLevels = 1;
	tdesc.MiscFlags = 0;
	tdesc.SampleDesc.Count = 1;
	tdesc.Usage = D3D11_USAGE_DEFAULT;
	tdesc.Width = cfg->cWidth;

	HR( Dev->CreateTexture2D( &tdesc, NULL, &tex ) );

	memset( &rtvdesc, 0, sizeof(rtvdesc) );
	rtvdesc.Format = DXGI_FORMAT_R32_FLOAT;
	rtvdesc.Texture2D.MipSlice = 0;
	rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

	Dev->CreateRenderTargetView( tex, &rtvdesc, &ShadowVolumeRTV );

	memset( &srvdesc, 0, sizeof(srvdesc) );
	srvdesc.Format = DXGI_FORMAT_R32_FLOAT;
	srvdesc.Texture2D.MipLevels = 1;
	srvdesc.Texture2D.MostDetailedMip = 0;
	srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;

	Dev->CreateShaderResourceView( tex, &srvdesc, &ShadowVolumeSRV );

	REL( tex );*/
	
	//===================================================
	//				DepthStencilStates
	//===================================================

	D3D11_DEPTH_STENCIL_DESC ddesc;

	memset( &ddesc, 0, sizeof(ddesc) );
	ddesc.DepthEnable = true;
	ddesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
	ddesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
	ddesc.StencilEnable = false;
	ddesc.StencilReadMask = 0;
	ddesc.StencilWriteMask = 0;

	HR( Dev->CreateDepthStencilState( &ddesc, &DSS_Tile ) );
		
	//===================================================
	//				Build meshes
	//===================================================

	CreateSphere( TPL_1, 6, false, 0, 64 );
	CreateSphere( TPL_2, 8, false, 0, 128 );
	CreateSphere( TPL_3, 12, false, 0, 256 );
	CreateSphere( TPL_4[0], 16, true, 0, 256 );
	CreateSphere( TPL_4[1], 16, true, 1, 256 );

	CreateSpherePatch_LOW( TPL_5, 0, 1, 4, 18 );
	CreateSpherePatch_LOW( TPL_6[0], 0, 2, 8, 10, 16 );
	CreateSpherePatch_LOW( TPL_6[1], 1, 2, 4, 12 );
	CreateSpherePatch_LOW( TPL_7[0], 0, 4, 16, 12, 12, false );
	CreateSpherePatch_LOW( TPL_7[1], 1, 4, 16, 12, 12, false );
	CreateSpherePatch_LOW( TPL_7[2], 2, 4, 12, 10, 16, true );
	CreateSpherePatch_LOW( TPL_7[3], 3, 4, 6, 12, 0xFFFFFFFF, true );

	//build L8 meshes for L > L8	
	TILEVERTEX_HIGH *Vtx = new TILEVERTEX_HIGH [nvtx], *vtx;
	UINT16 *Idx = new UINT16 [nidx], *idx;

	vtx = Vtx;
	idx = Idx;

	for( int j = 0; j < 3; j++ ) {
		CreateSpherePatch_HIGH( vtx, idx, 0, TRES[j].vdim, TRES[j].vdim );
		vtx += TRES[j].nvtx[0];
		idx += TRES[j].nidx[0];

		CreateSpherePatch_HIGH( vtx, idx, 7, TRES[j].vdim );
		vtx += TRES[j].nvtx[1];
		idx += TRES[j].nidx[1];
	}
	
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = nvtx*Stride_TILEVERTEX_HIGH;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &VB_TPL ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &IB ) );

	delete [ ] Vtx;
	delete [ ] Idx;

	InitTerrainShadows();
}

void TileRenderMgr::GlobalExit() {
	void DeleteTILEMESH1( TILEMESH1 &mesh );

	DeleteTILEMESH1( TPL_1 );
	DeleteTILEMESH1( TPL_2 );
	DeleteTILEMESH1( TPL_3 );
	DeleteTILEMESH1( TPL_4[0] );
	DeleteTILEMESH1( TPL_4[1] );
	DeleteTILEMESH1( TPL_5 );
	DeleteTILEMESH1( TPL_6[0] );
	DeleteTILEMESH1( TPL_6[1] );
	DeleteTILEMESH1( TPL_7[0] );
	DeleteTILEMESH1( TPL_7[1] );
	DeleteTILEMESH1( TPL_7[2] );
	DeleteTILEMESH1( TPL_7[3] );

	REL( VB_TPL );
	REL( IB );
	REL( CB_AtmParams );
	REL( CB_PlanetShadowParams );
	REL( CB_RingParams );
	REL( VSCB_Tile[0] );
	REL( VSCB_Tile[1] );
	REL( HSCB_Tile );
	REL( PSCB_Tile[0] );
	REL( PSCB_Tile[1] );

	REL( WaterMicroTex );
	REL( SS_HeightMap );
	REL( SS_TileTexture );
	REL( SS_MicroTexture );
	
	REL( IL_TILEVERTEX_LOW );
	IL_TILEVERTEX_HIGH.Release();
	REL( VS_Tile_Far[0] );
	REL( VS_Tile_Far[1] );

	REL( DSS_Tile );

	ExitTerrainShadows();
}

void DeleteTILEMESH1( TILEMESH1 &mesh ) {
	REL( mesh.VB );
	REL( mesh.IB );
}

void TileRenderMgr::CreateSphere( TILEMESH1 &mesh, UINT nrings, bool hemisphere, int which_half, int texres ) {
	//creates full sphere mesh for L1-L4

	mesh.nvtx = hemisphere ? nrings*(nrings+1)+2 : nrings*(2*nrings+1)+2;
	mesh.nidx = hemisphere ? 6*nrings*nrings : 12*nrings*nrings;

	TILEVERTEX_LOW *Vtx, *vtx;
	Vtx = new TILEVERTEX_LOW [mesh.nvtx];
	vtx = Vtx;

	WORD *Idx, *idx;
	Idx = new WORD [mesh.nidx];
	idx = Idx;

	WORD x, y, nvtx = 0, nidx = 0;
	double fDAng = PI/nrings;
	double fDAngY0 = fDAng;

	DWORD x1 = ( hemisphere ? nrings : nrings*2 );
	DWORD x2 = x1 + 1;

	double du = 0.5f/(double)texres;
	double a = (1.0f-2.0f*du)/(double)x1;
	double y0, r0, tv, fDAngX0, tu;
	D3DXVECTOR3 vec;

	//positions, normals and texcoords
	for( y = 0; y < nrings; y++ ) {
		y0 = cos(fDAngY0);
		r0 = sin(fDAngY0);
		tv = fDAngY0/PI;
		for( x = 0; x < x2; x++ ) {
			fDAngX0 = x*fDAng - PI;
			if( hemisphere && which_half )
				fDAngX0 += PI;
			vec = D3DXVECTOR3( float(r0*cos(fDAngX0)), float(y0), float(r0*sin(fDAngX0)) );
			tu = float(a*(double)x + du);
			*vtx = _TVTX_LOW( vec*1000.0f, vec, tu, tv );
			vtx->TangL = D3DXVECTOR3( float(-1.0*sin(fDAngX0)), 0.0f, float(cos(fDAngX0)) );
			vtx++;
			nvtx++;
		}
		fDAngY0 += fDAng;
	}

	for( y = 0; y < nrings-1; y++ ) {
		for( x = 0; x < x1; x++ ) {
			*idx++ = (WORD)( (y+0)*x2 + (x+0) );
			*idx++ = (WORD)( (y+0)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+0) );
			*idx++ = (WORD)( (y+0)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+1) );
			*idx++ = (WORD)( (y+1)*x2 + (x+0) );
			nidx += 6;
		}
	}

	vec = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
	WORD wNorthVtx = nvtx;

	*vtx++ = _TVTX_LOW( 1000.0f*vec, vec, 0.5f, 0.0f );
	nvtx++;

	vec = D3DXVECTOR3( 0.0f, -1.0f, 0.0f );
	WORD wSouthVtx = nvtx;
	*vtx++ = _TVTX_LOW( 1000.0f*vec, vec, 0.5f, 1.0f );
	nvtx++;

	for( x = 0; x < x1; x++ ) {
		*idx++ = wSouthVtx;
		*idx++ = (WORD)( y*x2 + x + 1 );
		*idx++ = (WORD)( y*x2 + x );
		nidx += 3;
	}

	for( x = 0; x < x1; x++ ) {
		*idx++ = wNorthVtx;
		*idx++ = (WORD)( x + 1 );
		*idx++ = (WORD)x;
		nidx += 3;
	}

	//tangents
	mesh.nface = mesh.nidx/3;

	//buffers
	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = mesh.nvtx*44;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Vtx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &mesh.VB ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = mesh.nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = Idx;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &mesh.IB ) );
	D3DXComputeBoundingSphere( (D3DXVECTOR3*)&Vtx->PosL, mesh.nvtx, Stride_TILEVERTEX_LOW, &mesh.bsPos, &mesh.bsRad );

	delete [ ] Vtx;
	delete [ ] Idx;
}

void TileRenderMgr::CreateSpherePatch_LOW( TILEMESH1 &mesh, UINT ilat, UINT nlat, UINT nlng, UINT res, UINT bseg, bool reduce, bool shift_origin ) {
	
	//tile angular dimesions
	double	minlat = PI05 * (double)ilat/(double)nlat,
			maxlat = PI05 * (double)(ilat+1)/(double)nlat,
			minlng = 0.0,
			maxlng = PI2/(double)nlng;
	
	//res	vertical
	//bseg	horizontal
	if( bseg < 0 || ilat == nlat-1 )
		bseg = (nlat-ilat)*res;

	mesh.nvtx = (bseg+1)*(res+1);
	if( reduce )
		mesh.nvtx -= ((res+1)*res)/2;

	TILEVERTEX_LOW *vdata = new TILEVERTEX_LOW [mesh.nvtx];

	//vertex buffer
	double lat, slat, clat, lng, slng, clng;
	int i, j, n, nseg;//counters.
	VECTOR3 pos, tpos;

	for( i = n = 0; i <= res; i++ ) {
		lat = minlat + (maxlat-minlat)*(double)i/(double)res;
		slat = sin(lat), clat = cos(lat);
		nseg = ( reduce ? bseg-i : bseg );
		for( j = 0; j <= nseg; j++ ) {
			lng = ( nseg ? minlng + (maxlng-minlng)*(double)j/(double)nseg : 0.0 );
			slng = sin(lng), clng = cos(lng);

			pos = _V( clat*clng, slat, clat*slng );

			vdata[n].PosL = D3DXVECTOR3( (float)pos.x, (float)pos.y, (float)pos.z );
			vdata[n].PosL *= 1000.0f;

			vdata[n].NormL = D3DXVECTOR3( (float)pos.x, (float)pos.y, (float)pos.z );

			vdata[n].TangL = D3DXVECTOR3( -slng, 0.0f, clng );

			vdata[n].TCrd.x = (float)( nseg ? (float)j/(float)nseg : 0.5f );
			vdata[n].TCrd.y = (float)( ((float)res-(float)i)/(float)res );

		//	vdata[n].mtex.x = (nseg ? vdata[n].tex.x * TEX2_MULTIPLIER : 0.5f);
		//	vdata[n].mtex.y = vdata[n].tex.y * TEX2_MULTIPLIER;
			n++;
		}
	}

	//index buffer:
	mesh.nidx = ( reduce ? res*(2*bseg-res) : 2*res*bseg )*3;
	WORD *idata = new WORD [mesh.nidx];
	int nofs0, nofs1;

	for( i = n = nofs0 = 0; i < res; i++ ) {
		nseg = ( reduce ? bseg-i : bseg );
		nofs1 = nofs0 + nseg + 1;
		for( j = 0; j < nseg; j++ ) {
			idata[n++] = nofs0 + j;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs0 + j + 1;
			if( reduce && j == nseg-1 )
				break;
			idata[n++] = nofs0 + j + 1;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs1 + j + 1;
		}
		nofs0 = nofs1;
	}

	//=================================
	D3D11_BUFFER_DESC bdesc;
	D3D11_SUBRESOURCE_DATA sdata;

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bdesc.ByteWidth = mesh.nvtx*Stride_TILEVERTEX_LOW;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = vdata;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &mesh.VB ) );

	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	bdesc.ByteWidth = mesh.nidx*2;
	bdesc.CPUAccessFlags = 0;
	bdesc.MiscFlags = 0;
	bdesc.StructureByteStride = 0;
	bdesc.Usage = D3D11_USAGE_IMMUTABLE;

	memset( &sdata, 0, sizeof(sdata) );
	sdata.pSysMem = idata;

	HR( Dev->CreateBuffer( &bdesc, &sdata, &mesh.IB ) );

	//compute tangents
	mesh.nface = mesh.nidx/3;

	D3DXComputeBoundingSphere( (D3DXVECTOR3*)vdata, mesh.nvtx, Stride_TILEVERTEX_LOW, &mesh.bsPos, &mesh.bsRad );

	delete [ ] vdata;
	delete [ ] idata;
}

void TileRenderMgr::CreateSpherePatch_HIGH( TILEVERTEX_HIGH *vtx, UINT16 *Idx, UINT ilat, UINT res, UINT bseg ) {

	int reduce = (ilat == 7 ? 1 : 0 );
	double	minlat = PI05 * (double)ilat/8.0,
			maxlat = PI05 * (double)(ilat+1)/8.0,
			minlng = 0.0,
			maxlng = PI2/32.0;

	if( bseg < 0 || reduce )
		bseg = (8-ilat)*res;

	UINT nvtx = (bseg+1)*(res+1);
	if( reduce )	nvtx -= ((res+1)*res)/2;

	TILEVERTEX_HIGH *vdata = vtx;

	//======================================
	//		Vertex buffer
	//======================================
	double lat, slat, clat, lng, slng, clng;
	int i, j, n, nseg;
	VECTOR3 pos, tpos;

	double lat_shift = reduce ? minlat : 0.0;

	double dlat = (maxlat - minlat)/res;
	lat = minlat;
	lng = minlng;

	for( i = n = 0; i <= res; i++ ) {

		if( i == 0 )			lat = minlat;
		else if( i == res )		lat = maxlat;
		else					lat = minlat + (maxlat-minlat)*(double)(i-1)/(double)(res-2+reduce);

		nseg = (reduce ? bseg - i : bseg);
		for( j = 0; j <= nseg; j++ ) {

			if( j == 0 )			lng = minlng;
			else if( j == nseg )	lng = maxlng;
			else					lng = ( nseg ? minlng + (maxlng-minlng)*(double)(j-1)/(double)(nseg-2+reduce) : 0.0 );

			vtx[n].PosL.x = lat - lat_shift;
			vtx[n].PosL.y = lng;
			
			if( j == 0 || j == nseg )	vtx[n].TCrd.x = (j == nseg ? 1.0f : 0.0f);
			else						vtx[n].TCrd.x = lng/(maxlng - minlng);

			if( i == 0 || i == res )	vtx[n].TCrd.y = (i == res ? 0.0f : 1.0f);
			else						vtx[n].TCrd.y = 1.0f - (lat - minlat)/(maxlat-minlat);

			vtx[n].flag = 0;

			if( j == 0 || j == nseg || i == 0 || i == res )		vtx[n].flag = 1;
			n++;
		}
	}

	//======================================
	//		Index buffer
	//======================================
	UINT nidx = ( reduce ? res*(2*bseg-res) : 2*res*bseg )*3;
	WORD *idata = Idx;
	int nofs0, nofs1;

	for( i = n = nofs0 = 0; i < res; i++ ) {
		nseg = ( reduce ? bseg-i : bseg );
		nofs1 = nofs0 + nseg + 1;
		for( j = 0; j < nseg; j++ ) {
			idata[n++] = nofs0 + j;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs0 + j + 1;
			if( reduce && j == nseg-1 )
				break;
			idata[n++] = nofs0 + j + 1;
			idata[n++] = nofs1 + j;
			idata[n++] = nofs1 + j + 1;
		}
		nofs0 = nofs1;
	}
}

TILEVERTEX_LOW _TVTX_LOW( D3DXVECTOR3 pos, D3DXVECTOR3 norm, float tu, float tv ) {
	TILEVERTEX_LOW out;
	out.PosL = pos;
	out.NormL = norm;
	out.TangL = D3DXVECTOR3( -1.0f, -1.0f, -1.0f );
	out.TCrd.x = tu;
	out.TCrd.y = tv;
	return out;
}

void TileRenderMgr::SetWaterMicroTexture( const char *fname ) {
	static bool water_micro_loaded = false;

	if( !fname || water_micro_loaded )	return;

	char cpath[256];
	FILE *file;
	HRESULT hr;
	DWORD key;
	BYTE *data;
	DWORD bufsize;

	if( !gc->TexturePath( fname, cpath ) )	return;
	if( !(file = fopen( cpath, "rb" ) ) )
		return;

	fread( &key, 4, 1, file );
	if( key != MAKEFOURCC( 'D', 'D', 'S', ' ' ) )
		return;

	fseek( file, 0, SEEK_END );
	bufsize = ftell( file );
	fseek( file, 0, SEEK_SET );
	data = new BYTE [bufsize+1];
	fread( data, 1, bufsize, file );
	fclose( file );

	D3DX11_IMAGE_INFO IInfo;
	D3DX11_IMAGE_LOAD_INFO LInfo;

	D3DX11GetImageInfoFromMemory( data, bufsize, NULL, &IInfo, &hr );
	if( hr != S_OK ) {
		delete [ ] data;
		return;
	}

	LInfo.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	LInfo.CpuAccessFlags = 0;
	LInfo.Depth = 1;
	LInfo.Filter = D3DX11_FILTER_NONE;
	LInfo.FirstMipLevel = 0;
	LInfo.Format = IInfo.Format;
	LInfo.MipFilter = D3DX11_FILTER_NONE;
	LInfo.MipLevels = 1;
	LInfo.MiscFlags = 0;
	LInfo.pSrcInfo = &IInfo;
	LInfo.Usage = D3D11_USAGE_IMMUTABLE;
	LInfo.Width = IInfo.Width;
	LInfo.Height = IInfo.Height;

	D3DX11CreateShaderResourceViewFromMemory( Dev, data, bufsize, &LInfo, NULL, &WaterMicroTex, &hr );
#if _DEBUG
	{	
		char buffer[200];
		buffer[199] = '\0';
		sprintf(buffer, "[TileRenderMgr::SetWaterMicroTexture]%s", fname);
		if (WaterMicroTex)
			WaterMicroTex->SetPrivateData(WKPDID_D3DDebugObjectName, 200, buffer);
	}
#endif
	delete [ ] data;

	water_micro_loaded = true;
}

CComPtrEx<ID3D11VertexShader>
	TileRenderMgr::VS_Shadow;
CComPtrEx<ID3D11GeometryShader>
	TileRenderMgr::GS_Shadow;
CComPtrEx<ID3D11PixelShader>
	TileRenderMgr::PS_Shadow;
CComPtrEx<ID3D11Buffer>
	TileRenderMgr::cb_per_frame,
	TileRenderMgr::cb_per_tile;

void TileRenderMgr::InitTerrainShadows()
{
	HRESULT hr;
	ID3DBlob *SBlob, *EBlob;

	::CompileFromFile( "Modules\\D3D11Shaders\\VS_Planet1_Shadow.fx", nullptr, NULL, "VS_Shadow", vs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateVertexShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &VS_Shadow ) );
	REL( SBlob );
	REL( EBlob );

	::CompileFromFile( "Modules\\D3D11Shaders\\GS_Planet1_Shadow.fx", nullptr, NULL, "GS_Shadow", gs_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreateGeometryShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &GS_Shadow ) );
	REL( SBlob );
	REL( EBlob );

/*	::CompileFromFile( "Modules\\D3D11Shaders\\PS_Planet1_Shadow.fx", nullptr, NULL, "PS_Shadow", ps_ver, ShaderCompileFlag, 0, NULL, &SBlob, &EBlob, &hr );
	ShowShaderCompilationError( hr, EBlob );
	HR( Dev->CreatePixelShader( SBlob->GetBufferPointer(), SBlob->GetBufferSize(), NULL, &PS_Shadow ) );
	REL( SBlob );
	REL( EBlob );
*/
	D3D11_BUFFER_DESC bdesc;
	memset( &bdesc, 0, sizeof(bdesc) );
	bdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	bdesc.ByteWidth = 16;
	bdesc.Usage = D3D11_USAGE_DEFAULT;
	Dev->CreateBuffer( &bdesc, nullptr, &cb_per_frame );

	bdesc.ByteWidth = 112;
	Dev->CreateBuffer( &bdesc, nullptr, &cb_per_tile );
}

void TileRenderMgr::ExitTerrainShadows()
{
	VS_Shadow.Release();
	GS_Shadow.Release();
//	PS_Shadow.Release();
	cb_per_frame.Release();
	cb_per_tile.Release();
}

void TileRenderMgr::RenderTerrainShadows( std::vector<TILERENDERSPEC> &trs, float dist_scale, float coef_altitude )
{
	UINT j;
	const UINT VBOffset = 0;
	
	CBShadow_per_frame cbpf;
	cbpf.dist_scale = dist_scale;
	cbpf.coef_altitude = coef_altitude;
	iCtx->UpdateSubresource( cb_per_frame, 0, nullptr, &cbpf, 0, 0 );

	iCtx->IASetInputLayout( IL_TILEVERTEX_HIGH );
	iCtx->IASetVertexBuffers( 0, 1, &VB_TPL, &Stride_TILEVERTEX_HIGH, &VBOffset );
	iCtx->IASetIndexBuffer( IB, DXGI_FORMAT_R16_UINT, 0 );
	iCtx->RSSetState( RS_CullNone_Solid );
	
	iCtx->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST );
	iCtx->VSSetShader( VS_Shadow, NULL, 0 );
	iCtx->VSSetSamplers( 0, 1, &SS_HeightMap );

	ID3D11Buffer *cb[2] = { cb_per_frame, cb_per_tile };
	iCtx->VSSetConstantBuffers( 0, 2, cb );

	iCtx->GSSetShader( GS_Shadow, nullptr, 0 );
	iCtx->GSSetConstantBuffers( 0, 1, &vVessel::cb_gs_ShadowMap );

	iCtx->PSSetShader( nullptr, nullptr, 0 );
	iCtx->OMSetBlendState( BS_NoBlend, D3DXVECTOR4( 1, 1, 1, 1 ), 0xFFFFFFFF );

	CBShadow_per_tile cbuf;
	for( j = 0; j < trs.size(); j++ ) {

		cbuf.mWorld = trs[j].mWorld;
		cbuf.coef_lat_0 = (float)(PI05*((double)trs[j].ilat/(double)trs[j].nlat));
		cbuf.coef_lat_1 = (float)(8.0/(double)trs[j].nlat);
		cbuf.coef_lng = (float)((8.0*4.0*(double)trs[j].nlat)/((double)trs[j].nlng*(double)trs[j].nlat));

		if( trs[j].nlat > 10 ) {
			cbuf.cos_minlat = (float)(1000.0*cos((PI05*(double)trs[j].ilat)/(double)trs[j].nlat));
			cbuf.sin_minlat = (float)(1000.0*sin((PI05*(double)trs[j].ilat)/(double)trs[j].nlat));
		}
		else
			cbuf.cos_minlat = cbuf.sin_minlat = 0.0f;

		if( !(CFG->bHeightMap && trs[j].Tex.htex) )
			continue;

		iCtx->VSSetShaderResources( 0, 1, trs[j].Tex.htex->GetTexture() );//VS if FL10

		//tex coord range for heightmap
		cbuf.TexOff_height[0] = trs[j].RANGE.range_height.tumax - trs[j].RANGE.range_height.tumin;
		cbuf.TexOff_height[1] = trs[j].RANGE.range_height.tumin;
		cbuf.TexOff_height[2] = trs[j].RANGE.range_height.tvmax - trs[j].RANGE.range_height.tvmin;
		cbuf.TexOff_height[3] = trs[j].RANGE.range_height.tvmin;
		cbuf.ncascade = trs[j].ncascade;

		iCtx->UpdateSubresource( cb_per_tile, 0, nullptr, &cbuf, 0, 0 );

		UINT R = (coef_altitude == 0.0f ? 0 : ( (trs[j].td->amax - trs[j].td->amin) > 0.35f*CFG->maxTerrainElevation ? 2 : 1 ));
		UINT P = trs[j].ilat+1 == trs[j].nlat;

		iCtx->DrawIndexed( TRES[R].nidx[P], TRES[R].sidx[P], TRES[R].svtx[P] );
	}

	iCtx->GSSetShader( nullptr, nullptr, 0 );
}
